import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer';

import {
  append as svgAppend,
  attr as svgAttr,
  classes as svgClasses,
  create as svgCreate,
  remove as svgRemove,
  clear as svgClear,
} from 'tiny-svg';

import { getRoundRectPath } from 'bpmn-js/lib/draw/BpmnRenderUtil';

import { is } from 'bpmn-js/lib/util/ModelUtil';
import { isAny } from 'bpmn-js/lib/features/modeling/util/ModelingUtil';
import TextUtil from 'diagram-js/lib/util/Text';
import { isObject, assign, forEach } from 'min-dash';

import { customElements, customConfig, hasLabelElements } from './util';

const HIGH_PRIORITY = 1500;
const ELEMENT_BORDER_RADIUS = 4;
const DEFAULT_FONT_SIZE = 12;
const LINE_HEIGHT_RATIO = 1.2;
const FILL = '#5c5638';

const defaultStyle = {
  fontFamily: 'Arial, sans-serif',
  fontSize: DEFAULT_FONT_SIZE,
  fontWeight: 'normal',
  fill: FILL,
  lineHeight: LINE_HEIGHT_RATIO,
};

const textUtil = new TextUtil({
  style: defaultStyle,
});

export default class CustomRenderer extends BaseRenderer {
  constructor(eventBus, bpmnRenderer) {
    super(eventBus, HIGH_PRIORITY);

    this.bpmnRenderer = bpmnRenderer;
  }

  canRender(element) {
    // only render tasks and events (ignore labels)
    return isAny(element, customElements) && !element.labelTarget;
  }

  drawShape(parentNode, element) {
    const { type } = element;
    const { url, iconAttr, shapeAttr, padding, align } = customConfig[type];
    const shape = this.bpmnRenderer.drawShape(parentNode, element);
    svgClear(parentNode);
    switch (type) {
      case 'bpmn:StartEvent':
        renderCircle(parentNode, element, shapeAttr, iconAttr, url);
        break;
      case 'bpmn:EndEvent':
        renderCircle(parentNode, element, shapeAttr, iconAttr, url);
        break;
      case 'bpmn:UserTask':
        renderRect(parentNode, element, shapeAttr, iconAttr, url, align, padding);
        break;
      case 'bpmn:ScriptTask':
        renderRect(parentNode, element, shapeAttr, iconAttr, url, align, padding);
        break;
      case 'bpmn:CallActivity':
        renderRect(parentNode, element, shapeAttr, iconAttr, url, align, padding);
        break;
      case 'bpmn:ExclusiveGateway':
        renderDiamond(parentNode, element, shapeAttr, iconAttr, url);
        break;
      case 'bpmn:ParallelGateway':
        renderDiamond(parentNode, element, shapeAttr, iconAttr, url);
        break;
      case 'bpmn:InclusiveGateway':
        renderDiamond(parentNode, element, shapeAttr, iconAttr, url);
        break;
      default:
        break;
    }

    return shape;
  }

  drawConnection(parentNode, element) {
    const shape = this.bpmnRenderer.drawConnection(parentNode, element);
    let eleId = shape.style['marker-end'].split('"')[1];
    let marker = document.querySelector(eleId);
    marker.childNodes[0].style.fill = '#989A9C';
    marker.childNodes[0].style.stroke = '#989A9C';
    svgAttr(shape, { stroke: '#989A9C' });
    return shape;
  }

  getShapePath(shape) {
    return this.bpmnRenderer.getShapePath(shape);
  }
}

CustomRenderer.$inject = ['eventBus', 'bpmnRenderer'];

function renderRect(parentGfx, element, shapeAttr, iconAttr, url, align, padding) {
  drawRect(parentGfx, shapeAttr.width, shapeAttr.height, ELEMENT_BORDER_RADIUS, null, {
    ...shapeAttr.style,
  });
  element.width = shapeAttr.width;
  element.height = shapeAttr.height;
  renderLabel(parentGfx, element.businessObject.name, {
    box: element,
    align,
    padding,
  });
  const customIcon = svgCreate('image', {
    ...iconAttr,
    href: url,
  });
  svgAppend(parentGfx, customIcon);
}

function renderDiamond(parentGfx, element, shapeAttr, iconAttr, url) {
  drawDiamond(parentGfx, shapeAttr.width, shapeAttr.height, { ...shapeAttr.style });
  element.width = shapeAttr.width;
  element.height = shapeAttr.height;
  const customIcon = svgCreate('image', {
    ...iconAttr,
    href: url,
  });
  svgAppend(parentGfx, customIcon);
}

function renderCircle(parentGfx, element, shapeAttr, iconAttr, url) {
  drawCircle(parentGfx, shapeAttr.width, shapeAttr.height, { ...shapeAttr.style });
  element.width = shapeAttr.width;
  element.height = shapeAttr.height;
  const customIcon = svgCreate('image', {
    ...iconAttr,
    href: url,
  });
  svgAppend(parentGfx, customIcon);
}

function renderLabel(parentGfx, label, options) {
  options = assign(
    {
      size: {
        width: 100,
      },
    },
    options,
  );
  var text = textUtil.createText(label || '', options);
  svgClasses(text).add('djs-label');
  svgAppend(parentGfx, text);
  return text;
}

// copied from 'bpmn-js/lib/draw/BpmnRenderer.js'
function drawCircle(parentGfx, width, height, offset, attrs) {
  if (isObject(offset)) {
    attrs = offset;
    offset = 0;
  }

  offset = offset || 0;

  attrs = assign(
    {
      stroke: 'black',
      strokeWidth: 2,
      fill: 'white',
    },
    attrs,
  );

  if (attrs.fill === 'none') {
    delete attrs.fillOpacity;
  }

  var cx = width / 2,
    cy = height / 2;

  var circle = svgCreate('circle');
  svgAttr(circle, {
    cx: cx,
    cy: cy,
    r: Math.round((width + height) / 4 - offset),
  });
  svgAttr(circle, attrs);

  svgAppend(parentGfx, circle);

  return circle;
}

function drawRect(parentGfx, width, height, r, offset, attrs) {
  if (isObject(offset)) {
    attrs = offset;
    offset = 0;
  }

  offset = offset || 0;

  // attrs = assign(attrs, {
  // 	stroke: "black",
  // 	strokeWidth: 2,
  // 	fill: "white"
  // });

  var rect = svgCreate('rect');
  svgAttr(rect, {
    x: offset,
    y: offset,
    width: width - offset * 2,
    height: height - offset * 2,
    rx: r,
    ry: r,
  });
  svgAttr(rect, attrs);

  svgAppend(parentGfx, rect);

  return rect;
}

function drawDiamond(parentGfx, width, height, attrs) {
  var x_2 = width / 2;
  var y_2 = height / 2;

  var points = [
    { x: x_2, y: 0 },
    { x: width, y: y_2 },
    { x: x_2, y: height },
    { x: 0, y: y_2 },
  ];

  var pointsString = points
    .map(function (point) {
      return point.x + ',' + point.y;
    })
    .join(' ');

  attrs = assign(
    {
      stroke: 'black',
      strokeWidth: 2,
      fill: 'white',
    },
    attrs,
  );

  var polygon = svgCreate('polygon');
  svgAttr(polygon, {
    points: pointsString,
  });
  svgAttr(polygon, attrs);

  svgAppend(parentGfx, polygon);

  return polygon;
}
